/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkStateGridA.h"
#include "MkSdp/MkSdp.h"
#include "MkEvent/MkTimer.h"
#include "MkUtil/MkUtil.h"
#include "MkUtil/MkLog.h"
#include <tinyxml2.h>

CMkStateGridA::CMkStateGridA(CMkEventPool* pEventPool, CMkTimer* pTimer)
    : CMkSipServer(pEventPool, pTimer)
{

}

Uint32 CMkStateGridA::Start(const MkString& LocalSipId, const MkString& Realm, const MkString& LocalHost, Uint16 Port, const MkStateGridARequestCallback& ReqCb)
{
    Uint32 ErrorCode = CMkSipServer::Start(LocalSipId, Realm, LocalHost, Port);
    MkCheckErrorLog(ErrorCode, "start sip failed\n");

    m_ReqCb = ReqCb;
    MkInfoLog("start state grid a server success\n");
    return NoneError;
}

Uint32 CMkStateGridA::Stop()
{
    return CMkSipServer::Stop();
}

Uint32 CMkStateGridA::RealPlay(const MkString& RemoteId, const MkString& TargetId, BOOL bSubStream, const MkString& RemoteRealm, const MkString& RemoteIp, Uint16 RemotePort, const MkString& RtpHost, Uint16 RtpPort, const MkStateGridAResponseCallback& ResCb)
{
    CMkSdp Sdp;
    Sdp.SetVersion("0");
    Sdp.SetOrigin(CMkOrigin("-", "0", "0", "IN", "IP4", RtpHost));
    Sdp.SetSessionName("-");
    Sdp.SetConnection(CMkSdpConnection("IN", "IP4", RtpHost));

    CMkSdpMedia SdpMedia;
    SdpMedia.SetMediaType("video");
    SdpMedia.SetPort(RtpPort);
    SdpMedia.SetProtocol("RTP/AVP");
    SdpMedia.SetSubType(bSubStream ? "sub" : "main");
    CMkSdpFmt Fmt(100, "H264", 90000);
    Fmt.SetFmtpNode("CIF", "1");
    Fmt.SetFmtpNode("4CIF", "1");
    Fmt.SetFmtpNode("F", "1");
    Fmt.SetFmtpNode("K", "1");
    SdpMedia.AddFmt(Fmt);
    SdpMedia.AddAttr(CMkSdpAttribute("recvonly"));
    Sdp.AddMedia(SdpMedia);

    MkString strSdp;
    Sdp.ToString(strSdp);

    return SendSipRequest(RemoteId, TargetId, RemoteRealm, MkSipMethodInvite, RemoteIp, RemotePort, FALSE, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    }, MkMapString(), MkSipContentSdp, strSdp, "");
}

Uint32 CMkStateGridA::PtzControl(const MkString& RemoteId, const MkString& TargetId, const MkString& RemoteRealm, const MkString& RemoteIp, Uint16 RemotePort, Uint32 Cmd, Uint32 Param1, Uint32 Param2, Uint32 Param3, const MkStateGridAResponseCallback& ResCb)
{
    MkString strXml;
    tinyxml2::XMLDocument doc;
    doc.InsertFirstChild(doc.NewDeclaration());

    tinyxml2::XMLElement *root = doc.NewElement("SIP_XML");
    root->SetAttribute("EventType", "Control_Camera");
    doc.InsertEndChild(root);

    tinyxml2::XMLElement *ItemNode = doc.NewElement("Item");
    ItemNode->SetAttribute("Command", std::to_string(Cmd).c_str());
    ItemNode->SetAttribute("CommandPara1", std::to_string(Param1).c_str());
    ItemNode->SetAttribute("CommandPara2", std::to_string(Param2).c_str());
    ItemNode->SetAttribute("CommandPara3", std::to_string(Param3).c_str());
    root->InsertEndChild(ItemNode);
    tinyxml2::XMLPrinter printer;
    doc.Print(&printer);
    strXml = printer.CStr();

    return SendSipRequest(RemoteId, TargetId, RemoteRealm, MkSipMethodMessage, RemoteIp, RemotePort, FALSE, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    }, MkMapString(), MkSipContentXml, strXml, "");
}

Uint32 CMkStateGridA::Ptz3DZoom(const MkString& RemoteId, const MkString& TargetId, const MkString& RemoteRealm, const MkString& RemoteIp, Uint16 RemotePort, Uint32 Cmd, Uint32 Param1, Uint32 Param2, Uint32 Param3, Uint32 Param4, Uint32 Param5, Uint32 Param6, Uint32 Param7, const MkStateGridAResponseCallback& ResCb)
{
    MkString strXml;
    tinyxml2::XMLDocument doc;
    doc.InsertFirstChild(doc.NewDeclaration());

    tinyxml2::XMLElement *root = doc.NewElement("SIP_XML");
    root->SetAttribute("EventType", "3Dcontrol_Camera");
    doc.InsertEndChild(root);

    tinyxml2::XMLElement *ItemNode = doc.NewElement("Item");
    ItemNode->SetAttribute("Code", TargetId.c_str());
    ItemNode->SetAttribute("Command", std::to_string(Cmd).c_str());
    ItemNode->SetAttribute("Para1", std::to_string(Param1).c_str());
    ItemNode->SetAttribute("Para2", std::to_string(Param2).c_str());
    ItemNode->SetAttribute("Para3", std::to_string(Param3).c_str());
    ItemNode->SetAttribute("Para4", std::to_string(Param4).c_str());
    ItemNode->SetAttribute("Para5", std::to_string(Param5).c_str());
    ItemNode->SetAttribute("Para6", std::to_string(Param6).c_str());
    ItemNode->SetAttribute("Para7", std::to_string(Param7).c_str());
    root->InsertEndChild(ItemNode);
    tinyxml2::XMLPrinter printer;
    doc.Print(&printer);
    strXml = printer.CStr();

    return SendSipRequest(RemoteId, TargetId, RemoteRealm, MkSipMethodMessage, RemoteIp, RemotePort, FALSE, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    }, MkMapString(), MkSipContentXml, strXml, "");
}

Uint32 CMkStateGridA::AlarmSubscribe(const MkString& RemoteId, const MkString& TargetId, const MkString& RemoteRealm, const MkString& RemoteIp, Uint16 RemotePort, Uint32 Expries, const MkMap<MkString, Uint32>& MapSubId, const MkStateGridAResponseCallback& ResCb)
{
    MkString strXml;
    tinyxml2::XMLDocument doc;
    doc.InsertFirstChild(doc.NewDeclaration());

    tinyxml2::XMLElement *root = doc.NewElement("SIP_XML");
    root->SetAttribute("EventType", "Subscribe_Alarm");
    doc.InsertEndChild(root);

    MkMap<MkString, Uint32>::const_iterator it = MapSubId.begin();
    for (; it != MapSubId.end(); it++) {
        tinyxml2::XMLElement *ItemNode = doc.NewElement("Item");
        ItemNode->SetAttribute("Code", it->first.c_str());
        ItemNode->SetAttribute("Type", std::to_string(it->second).c_str());
        root->InsertEndChild(ItemNode);
    }

    tinyxml2::XMLPrinter printer;
    doc.Print(&printer);
    strXml = printer.CStr();

    MkMapString OtherHeader;
    OtherHeader["Event"] = "alarm";
    OtherHeader["Expires"] = std::to_string(Expries);

    return SendSipRequest(RemoteId, TargetId, RemoteRealm, MkSipMethodSubscribe, RemoteIp, RemotePort, FALSE, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    }, OtherHeader, MkSipContentXml, strXml, "");
}

Uint32 CMkStateGridA::StatusSubscribe(const MkString& RemoteId, const MkString& TargetId, const MkString& RemoteRealm, const MkString& RemoteIp, Uint16 RemotePort, Uint32 Expries, const MkVectorString& VecSubId, const MkStateGridAResponseCallback& ResCb)
{
    MkString strXml;
    tinyxml2::XMLDocument doc;
    doc.InsertFirstChild(doc.NewDeclaration());

    tinyxml2::XMLElement *root = doc.NewElement("SIP_XML");
    root->SetAttribute("EventType", "Subscribe_Status");
    doc.InsertEndChild(root);

    MkVectorString::const_iterator it = VecSubId.begin();
    for (; it != VecSubId.end(); it++) {
        tinyxml2::XMLElement *ItemNode = doc.NewElement("Item");
        ItemNode->SetAttribute("Code", it->c_str());
        root->InsertEndChild(ItemNode);
    }

    tinyxml2::XMLPrinter printer;
    doc.Print(&printer);
    strXml = printer.CStr();

    MkMapString OtherHeader;
    OtherHeader["Event"] = "presence";
    OtherHeader["Expires"] = std::to_string(Expries);

    return SendSipRequest(RemoteId, TargetId, RemoteRealm, MkSipMethodSubscribe, RemoteIp, RemotePort, FALSE, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    }, OtherHeader, MkSipContentXml, strXml, "");
}

Uint32 CMkStateGridA::StopAlarmSubscirbe(const MkString& SubscribeId, const MkStateGridAResponseCallback& ResCb)
{
    MkMapString OtherHeaders;
    OtherHeaders["Expires"] = "0";
    OtherHeaders["Event"] = "alarm";
    return StopSubscribe(SubscribeId, OtherHeaders, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    });
}

Uint32 CMkStateGridA::StopStatusSubscirbe(const MkString& SubscribeId, const MkStateGridAResponseCallback& ResCb)
{
    MkMapString OtherHeaders;
    OtherHeaders["Expires"] = "0";
    OtherHeaders["Event"] = "presence";
    return StopSubscribe(SubscribeId, OtherHeaders, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    });
}

Uint32 CMkStateGridA::StopRealPlay(const MkString& SessionId, const MkStateGridAResponseCallback& ResCb)
{
    return StopCall(SessionId, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    });
}

Uint32 CMkStateGridA::AlarmNotify(const MkString& SubId, const MkString& StrState, const MkString& Code, const MkString& Name, const Uint32 AlarmType, const CMkTime& StartTime, const CMkTime& EndTime, const MkStateGridAResponseCallback& ResCb)
{
    MkString strXml;
    tinyxml2::XMLDocument doc;
    doc.InsertFirstChild(doc.NewDeclaration());

    tinyxml2::XMLElement *root = doc.NewElement("SIP_XML");
    root->SetAttribute("EventType", "Notify_Alarm");
    doc.InsertEndChild(root);

    tinyxml2::XMLElement *ItemNode = doc.NewElement("Item");
    ItemNode->SetAttribute("Code", Code.c_str());
    ItemNode->SetAttribute("Name", Name.c_str());
    ItemNode->SetAttribute("Type", std::to_string(AlarmType).c_str());
    MkString strStartTime;
    StartTime.GetString(strStartTime, "T", "Z");
    ItemNode->SetAttribute("StartTime", strStartTime.c_str());
    if (!EndTime.IsEmpty()) {
        MkString strEndTime;
        EndTime.GetString(strEndTime, "T", "Z");
        ItemNode->SetAttribute("EndTime", strEndTime.c_str());
    }
    root->InsertEndChild(ItemNode);

    tinyxml2::XMLPrinter printer;
    doc.Print(&printer);
    strXml = printer.CStr();

    return SubscribeNotify(SubId, "alarm", StrState, MkMapString(), MkSipContentXml, strXml, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    });
}

Uint32 CMkStateGridA::StateNotify(const MkString& SubId, const MkString& StrState, const MkString& Code, const MkString& Name, Uint32 Status, const MkStateGridAResponseCallback& ResCb)
{
    MkString strXml;
    tinyxml2::XMLDocument doc;
    doc.InsertFirstChild(doc.NewDeclaration());

    tinyxml2::XMLElement *root = doc.NewElement("SIP_XML");
    root->SetAttribute("EventType", "Notify_Status");
    doc.InsertEndChild(root);

    tinyxml2::XMLElement *ItemNode = doc.NewElement("Item");
    ItemNode->SetAttribute("Code", Code.c_str());
    ItemNode->SetAttribute("Name", Name.c_str());
    ItemNode->SetAttribute("Status", std::to_string(Status).c_str());
    root->InsertEndChild(ItemNode);

    tinyxml2::XMLPrinter printer;
    doc.Print(&printer);
    strXml = printer.CStr();

    return SubscribeNotify(SubId, "presence", StrState, MkMapString(), MkSipContentXml, strXml, [this, ResCb](const CMkSipMessage& SipMessage) {
        AddRequestToMap(SipMessage.GetTransactionId(), ResCb);
    });
}

Uint32 CMkStateGridA::OnSipRequest(const MkSipRequestEvent& RequestEvent, const CMkSipMessage& SipRequest, const EventRequestCallback& Cb)
{
    if (!Cb) {
        return InvalidParams;
    }
    Uint32 ErrorCode = NoneError;
    MkMapString ExternHeaders;
    if (!m_ReqCb) {
        Cb(MkSipStatusBadRequest, ExternHeaders, MkSipContentNone, "");
        return NoneError;
    }

    switch (RequestEvent) {
    case MkSipRequestMessage:
        ErrorCode = MessageRequestParse(SipRequest, Cb);
        break;
    case MkSipRequestSubscribe:
        ErrorCode = SubscribeRequestParse(SipRequest, Cb);
        break;
    case MkSipRequestSubscribeNotify:
        ErrorCode = SubscribeNotifyParse(SipRequest, Cb);
        break;
    case MkSipRequestInvite:
        ErrorCode = InviteRequestParse(SipRequest, Cb);
        break;
    case MkSipRequestBye:
        ErrorCode = ByeParse(SipRequest, Cb);
        break;
    default:
        ExternHeaders["Allow"] = "INVITE MESSAGE SUBSCRIBE NOTIFY BYE";
        Cb(MkSipStatusMethodNotAllowed, ExternHeaders, MkSipContentNone, "");
        break;
    }
    if (NoneError != ErrorCode) {
        Cb(MkSipStatusBadRequest, ExternHeaders, MkSipContentNone, "");
    }
    return NoneError;
}

Uint32 CMkStateGridA::OnSipResponse(const MkSipResponseEvent& ResponseEvent, const MkString& TransActionId, const CMkSipMessage& SipRequest, const CMkSipMessage& SipResponse, const EventResponseCallback& Cb)
{
    Uint32 ErrorCode = NoneError;
    switch (ResponseEvent) {
    case MkSipResponseMessage:
    case MkSipResponseBye:
    case MkSipResponseSubscribeNotify:
        ErrorCode = ResponseCallback(SipResponse.GetStatusCode(), SipRequest.GetTransactionId(), nullptr);
        break;
    case MkSipResponseInvite:
        ErrorCode = InviteResponseParse(SipRequest, SipResponse, Cb);
        break;
    case MkSipResponseSubscribe:
    {
        MkString strValue = "0";
        SipResponse.GetHeaderValue("Expires", strValue);
        CMkStateGridASubscribeResponse Res(SipRequest.GetDialogId(), std::stoi(strValue));
        ErrorCode = ResponseCallback(SipResponse.GetStatusCode(), SipRequest.GetTransactionId(), &Res);
    }
    break;
    default:
        ErrorCode = UnSupported;
        break;
    }
    return ErrorCode;
}

Uint32 CMkStateGridA::MessageRequestParse(const CMkSipMessage& SipRequest, const EventRequestCallback& Cb)
{
    MkString strXml = SipRequest.GetBody();
    tinyxml2::XMLDocument doc;
    Uint32 ErrorCode = doc.Parse(strXml.c_str());
    if (tinyxml2::XML_SUCCESS != ErrorCode) {
        return ErrorCode;
    }
    tinyxml2::XMLElement *Element = doc.FirstChildElement("SIP_XML");
    if (!Element) {
        return SystemError;
    }
    MkString EventType = CMkUtil::SafeMkString(Element->Attribute("EventType"));
    tinyxml2::XMLElement *ItemNode = Element->FirstChildElement("Item");
    if (!ItemNode) {
        return SystemError;
    }
    if ("Control_Camera" == EventType) {
        ErrorCode = PtzControlRequestParse(SipRequest.GetUri().GetUserName(), (MkStateGridPtzCommandType)CMkUtil::SafeAtoi(ItemNode->Attribute("Command")), CMkUtil::SafeAtoi(ItemNode->Attribute("CommandPara1")), CMkUtil::SafeAtoi(ItemNode->Attribute("CommandPara2")), CMkUtil::SafeAtoi(ItemNode->Attribute("CommandPara3")), 0, 0, 0, 0, Cb);
    } else if ("3Dcontrol_Camera" == EventType) {
        ErrorCode = PtzControlRequestParse(SipRequest.GetUri().GetUserName(), (MkStateGridPtzCommandType)CMkUtil::SafeAtoi(ItemNode->Attribute("Command")), CMkUtil::SafeAtoi(ItemNode->Attribute("Para1")), CMkUtil::SafeAtoi(ItemNode->Attribute("Para2")), CMkUtil::SafeAtoi(ItemNode->Attribute("Para3")), CMkUtil::SafeAtoi(ItemNode->Attribute("Para4")), CMkUtil::SafeAtoi(ItemNode->Attribute("Para5")), CMkUtil::SafeAtoi(ItemNode->Attribute("Para6")), CMkUtil::SafeAtoi(ItemNode->Attribute("Para7")), Cb);
    }
    return ErrorCode;
}

Uint32 CMkStateGridA::SubscribeRequestParse(const CMkSipMessage& SipRequest, const EventRequestCallback& Cb)
{
    MkString strEvent;
    MkString strExpires;
    Uint32 Expires = 3600;
    Uint32 ErrorCode = SipRequest.GetHeaderValue("Event", strEvent);
    MkCheckErrorLog(ErrorCode, "get event header failed\n");

    ErrorCode = SipRequest.GetHeaderValue("Expires", strExpires);
    if (NoneError == ErrorCode) {
        Expires = stoi(strExpires);
    }

    MkMap<MkString, Uint32> MapCode;
    MkString EventType;
    if (0 != Expires) {
        tinyxml2::XMLDocument doc;
        Uint32 ErrorCode = doc.Parse(SipRequest.GetBody().c_str());
        if (tinyxml2::XML_SUCCESS != ErrorCode) {
            return ErrorCode;
        }
        tinyxml2::XMLElement *Element = doc.FirstChildElement("SIP_XML");
        if (!Element) {
            return SystemError;
        }
        EventType = CMkUtil::SafeMkString(Element->Attribute("EventType"));
        tinyxml2::XMLElement *ItemNode = Element->FirstChildElement("Item");
        while (ItemNode) {
            MapCode[CMkUtil::SafeMkString(ItemNode->Attribute("Code"))] = CMkUtil::SafeAtoi(ItemNode->Attribute("Type"));
            ItemNode = ItemNode->NextSiblingElement();
        }
    }
    CMkStateGridASubscribeRequest Req(SipRequest.GetDialogId(), Expires, "Subscribe_Alarm" == EventType ? MkStateGridASubscribeAlarm : MkStateGridASubscribeStatus, MapCode);
    return m_ReqCb(&Req, [Cb, Expires](const MkSipStatus& StatusCode, const CMkStateGridAResponseBase* pResBase) {
        MkMapString OtherHeader;
        OtherHeader["Expires"] = std::to_string(Expires);
        Cb(StatusCode, OtherHeader, MkSipContentNone, "");
    });
}

Uint32 CMkStateGridA::InviteRequestParse(const CMkSipMessage& SipRequest, const EventRequestCallback& Cb)
{
    MkString SdpBody = SipRequest.GetBody();
    MkString ChannelId = SipRequest.GetUri().GetUserName();
    MkString SessionId = SipRequest.GetDialogId();
    CMkSdp Sdp;
    Sdp.ParseContent(SdpBody);
    MkString RtpHost = Sdp.GetOrigin().GetAddr();
    if (!Sdp.GetConnection().GetAddr().empty()) {
        RtpHost = Sdp.GetConnection().GetAddr();
    }
    Uint16 RtpPort = 0;
    Uint8 Payload = 0;
    MkString SubType;
    MkVector<CMkSdpMedia> VecSdpMedia = Sdp.GetMedia();
    if (VecSdpMedia.empty()
        || VecSdpMedia.begin()->GetFmts().empty()) {
        return InvalidParams;
    }
    RtpPort = VecSdpMedia.begin()->GetPort();
    Payload = VecSdpMedia.begin()->GetFmts().begin()->first;
    SubType = VecSdpMedia.begin()->GetSubType();
    CMkStateGridAMediaRequest Req(SessionId, ChannelId, RtpHost, RtpPort, Payload, "sub" == SubType);
    return m_ReqCb(&Req, [Cb, Payload](const MkSipStatus& StatusCode, const CMkStateGridAResponseBase* pResBase) {
        MkString strSdp;
        const CMkStateGridAMediaResponse* pRes = dynamic_cast<const CMkStateGridAMediaResponse*>(pResBase);
        if (pRes) {
            CMkSdp SdpRes;
            SdpRes.SetVersion("0");
            SdpRes.SetOrigin(CMkOrigin("MkStateGridAChina", "0", "0", "IN", "IP4", pRes->GetRtpHost()));
            SdpRes.SetConnection(CMkSdpConnection("IN", "IP4", pRes->GetRtpHost()));
            SdpRes.SetTime(CMkSdpTime(0, 0, MkVectorString()));
            SdpRes.SetSessionName("-");

            CMkSdpMedia SdpMedia;
            SdpMedia.SetMediaType("video");
            SdpMedia.SetPort(pRes->GetRtpPort());
            SdpMedia.SetProtocol("RTP/AVP");
            SdpMedia.AddFmt(CMkSdpFmt(Payload, "H.264-PS", 90000));
            SdpMedia.AddAttr(CMkSdpAttribute("sendonly"));
            SdpRes.AddMedia(SdpMedia);
            SdpRes.ToString(strSdp);
        }
        Cb(StatusCode, MkMapString(), MkSipContentSdp, strSdp);
    });
}

Uint32 CMkStateGridA::SubscribeNotifyParse(const CMkSipMessage& SipRequest, const EventRequestCallback& Cb)
{
    MkString strEvent;
    MkString strState;
    Uint32 ErrorCode = SipRequest.GetHeaderValue("Event", strEvent);
    MkCheckErrorLog(ErrorCode, "get event header failed\n");

    ErrorCode = SipRequest.GetHeaderValue("Subscription-State", strState);
    MkCheckErrorLog(ErrorCode, "get expires header failed\n");

    if ("alarm" == strEvent) {
        ErrorCode = NotifyAlarmRequestParse(strState, SipRequest.GetBody(), Cb);
    } else if ("presence" == strEvent) {
        ErrorCode = NotifyStatusRequst(strState, SipRequest.GetBody(), Cb);
    } else {
        ErrorCode = SystemError;
    }
    return ErrorCode;
}

Uint32 CMkStateGridA::ByeParse(const CMkSipMessage& SipRequest, const EventRequestCallback& Cb)
{
    MkString SessionId = SipRequest.GetDialogId();
    CMkStateGridAMediaShutDownRequest Req(SessionId);
    return m_ReqCb(&Req, [Cb](const MkSipStatus& StatusCode, const CMkStateGridAResponseBase* pResBase) {
        Cb(StatusCode, MkMapString(), MkSipContentNone, "");
    });
}

Uint32 CMkStateGridA::InviteResponseParse(const CMkSipMessage& SipRequest, const CMkSipMessage& SipResponse, const EventResponseCallback& Cb)
{
    MkString TransId = SipRequest.GetTransactionId();
    if (MkSipStatusOK != SipResponse.GetStatusCode()) {
        return ResponseCallback(SipResponse.GetStatusCode(), TransId, nullptr);
    }

    MkString SdpBody = SipResponse.GetBody();
    CMkSdp Sdp;
    Sdp.ParseContent(SdpBody);
    MkString RtpHost = Sdp.GetOrigin().GetAddr();
    if (!Sdp.GetConnection().GetAddr().empty()) {
        RtpHost = Sdp.GetConnection().GetAddr();
    }
    Uint16 RtpPort = 0;
    MkVector<CMkSdpMedia> VecSdpMedia = Sdp.GetMedia();
    if (VecSdpMedia.empty()) {
        return ResponseCallback(MkSipStatusBadRequest, TransId, nullptr);
    }
    RtpPort = VecSdpMedia.begin()->GetPort();
    CMkStateGridAMediaResponse Res(SipRequest.GetDialogId(), RtpHost, RtpPort, Cb);
    return ResponseCallback(SipResponse.GetStatusCode(), TransId, &Res);
}

Uint32 CMkStateGridA::PtzControlRequestParse(const MkString& ChannelId, MkStateGridPtzCommandType Cmd, Uint32 Param1, Uint32 Param2, Uint32 Param3, Uint32 Param4, Uint32 Param5, Uint32 Param6, Uint32 Param7, const EventRequestCallback& EventCallback)
{
    CMkStateGridAPtzControlRequest Req(ChannelId, Cmd, Param1, Param2, Param3, Param4, Param5, Param6, Param7);
    return m_ReqCb(&Req, [EventCallback](const MkSipStatus& StatusCode, const CMkStateGridAResponseBase* pResBase) {
        EventCallback(StatusCode, MkMapString(), MkSipContentNone, "");
    });
}

Uint32 CMkStateGridA::NotifyAlarmRequestParse(const MkString& StrState, const MkString& XmlBody, const EventRequestCallback& EventCallback)
{
    tinyxml2::XMLDocument doc;
    Uint32 ErrorCode = doc.Parse(XmlBody.c_str());
    if (tinyxml2::XML_SUCCESS != ErrorCode) {
        return ErrorCode;
    }
    tinyxml2::XMLElement *Element = doc.FirstChildElement("SIP_XML");
    if (!Element) {
        return InvalidParams;
    }
    MkString EventType = CMkUtil::SafeMkString(Element->Attribute("EventType"));
    tinyxml2::XMLElement *ItemNode = Element->FirstChildElement("Item");
    if ("Notify_Alarm" != EventType || !ItemNode) {
        return InvalidParams;
    }

    CMkStateGridAAlarmNotifyRequest Req(StrState, CMkUtil::SafeMkString(ItemNode->Attribute("Code")), CMkUtil::SafeMkString(ItemNode->Attribute("Name")), CMkUtil::SafeAtoi(ItemNode->Attribute("Type")), CMkTime(CMkUtil::SafeMkString(ItemNode->Attribute("StartTime"))), CMkTime(CMkUtil::SafeMkString(ItemNode->Attribute("EndTime"))));
    return m_ReqCb(&Req, [EventCallback](const MkSipStatus& StatusCode, const CMkStateGridAResponseBase* pResBase) {
        EventCallback(StatusCode, MkMapString(), MkSipContentNone, "");
    });
}

Uint32 CMkStateGridA::NotifyStatusRequst(const MkString& StrState, const MkString& XmlBody, const EventRequestCallback& EventCallback)
{
    tinyxml2::XMLDocument doc;
    Uint32 ErrorCode = doc.Parse(XmlBody.c_str());
    if (tinyxml2::XML_SUCCESS != ErrorCode) {
        return ErrorCode;
    }
    tinyxml2::XMLElement *Element = doc.FirstChildElement("SIP_XML");
    if (!Element) {
        return InvalidParams;
    }
    MkString EventType = CMkUtil::SafeMkString(Element->Attribute("EventType"));
    tinyxml2::XMLElement *ItemNode = Element->FirstChildElement("Item");
    if ("Notify_Status" != EventType || !ItemNode) {
        return InvalidParams;
    }

    CMkStateGridAStatusNotifyRequest Req(StrState, CMkUtil::SafeMkString(ItemNode->Attribute("Code")), CMkUtil::SafeMkString(ItemNode->Attribute("Name")), CMkUtil::SafeAtoi(ItemNode->Attribute("Status")));
    return m_ReqCb(&Req, [EventCallback](const MkSipStatus& StatusCode, const CMkStateGridAResponseBase* pResBase) {
        EventCallback(StatusCode, MkMapString(), MkSipContentNone, "");
    });
}

Uint32 CMkStateGridA::ResponseCallback(const MkSipStatus& StatusCode, const MkString& strKey, const CMkStateGridAResponseBase* pRes)
{
    MkStateGridAResponseCallback ResCb;
    m_ReqLock.WLock();
    MkMap<MkString, MkSGARequestParam>::iterator it = m_MapRequst.find(strKey);
    if (it != m_MapRequst.end()) {
        ResCb = it->second.ResCb;
        m_MapRequst.erase(it);
    }
    m_ReqLock.WUnlock();
    if (ResCb) {
        ResCb(StatusCode, pRes);
    }
    return NoneError;
}

Uint32 CMkStateGridA::AddRequestToMap(const MkString& Key, const MkStateGridAResponseCallback& ResCb)
{
    m_ReqLock.WLock();
    MkSGARequestParam Param;
    Param.RequestTime = NowMkTime;
    Param.ResCb = ResCb;
    Param.StrKey = Key;
    m_MapRequst[Param.StrKey] = Param;
    m_ReqLock.WUnlock();
    return NoneError;
}

CMkStateGridA::~CMkStateGridA()
{
    Stop();
}